#include "Level.h"

Level::Level(void) {
	levelNow = 0;
	levelNum = 0;
	bossState = 0;
	numMinions = 0;
	pInput = Input::getInstance();
	pEngine = DX9Engine::getInstance();
	state = WAIT;
	minionState = 0;
	bgOn = true;
	bossSpawn = false;
	m_Boss = 0;
}

Level::~Level(void) {
	Release();
}

void Level::Init() {
	setBG(L"images/Sankarea2.jpg");
	/**Jerry was here****************************************************************/
	// Physic System
	m_PhysicSystem.Initialize();
	gameover = false;
	//Player Entity
	p.initialize(L"images/player2.png", L"images/bullet2.png", &m_PhysicSystem);
	p.setData(V2DF(100.0f, 100.0f), V2DF(50.0f, 0.0f), 1.0f, 0.0f, 300.0f, 0);
	p.m_PhysicObject.m_physic_object_type = PHYSIC_OBJECT_TYPE_CIRCLE;
	p.m_PhysicObject.m_radius = 12.0f;
	p.setLives(5);
	//p.m_PhysicObject.m_physic_object_type = PHYSIC_OBJECT_TYPE_SQUARE;
	//p.m_PhysicObject.m_height = 32.0f;
	//p.m_PhysicObject.m_width = 32.0f;

	////Enemy Entity
	//e.initialize(L"images/Lv1Boss.png");
	//e.setData(V2DF(300.0f, 300.0f), V2DF((float)(rand()%100-50), (float)(rand()%100-50)), 1.0f, 0.0f, 50.0f, 1);
	////e.m_PhysicObject.m_physic_object_type = PHYSIC_OBJECT_TYPE_CIRCLE;
	////e.m_PhysicObject.m_radius = 16.0f;
	//e.m_PhysicObject.m_physic_object_type = PHYSIC_OBJECT_TYPE_SQUARE;
	//e.m_PhysicObject.m_height = 200.0f;
	//e.m_PhysicObject.m_width = 140.0f;


	// Physic System now has "control" over our Physic Object which is a part of Entity.
	// The Physic System does NOT own this Physic Object so we will have to release this 
	// Physic Object
	m_PhysicSystem.Create(&(p.m_PhysicObject));

	// Give the Player Entity a pointer to Physic System
	p.m_PhysicSystem = &m_PhysicSystem;

	// Physic System now has "control" over our Enemy's Physic Object
	//	m_PhysicSystem.Create(&(e.m_PhysicObject));

	lvl = ONE;
}

void Level::Release() {
	p.release();
	//e.release();
	m_PhysicSystem.Release();
	int t_size = m_squadList.Get_Size();
	for(int i = 0; i < t_size; i++){
		m_squadList.Get_Data(i)->Release();
	}
	m_squadList.Clear();

	t_size = playerBulletList.Get_Size();
	for(int i = 0; i < t_size; i++){
		playerBulletList.Get_Data(i)->Release();
	}
	playerBulletList.Clear();

	t_size = enemyBulletList.Get_Size();
	for(int i = 0; i < t_size; i++){
		enemyBulletList.Get_Data(i)->Release();
	}
	enemyBulletList.Clear();
	if(m_Boss){
		m_PhysicSystem.Destroy(&(m_Boss->m_PhysicObject));
		m_Boss->release();
		delete m_Boss;
		m_Boss = 0;
	}
}

void Level::setBoss() {
	//set the boss stats(hp,speed)
	m_Boss = new Boss;
	m_Boss->initialize(L"images/Lv1Boss.png", L"images/pew.png", &m_PhysicSystem, 800, 600);
	m_Boss->setData(V2DF(800.0f, 300.0f), V2DF((float)(-100), 0.0f), 1.0f, 0.0f, 50.0f, 1);
	m_Boss->m_PhysicObject.m_physic_object_type = PHYSIC_OBJECT_TYPE_SQUARE;
	m_Boss->m_PhysicObject.m_height = 200.0f;
	m_Boss->m_PhysicObject.m_width = 140.0f;
	m_Boss->setHP(100);
	m_Boss->SET_AI(false);
	m_PhysicSystem.Create(&(m_Boss->m_PhysicObject));
}

void Level::startLevel() {
	//init the current level
	p.setData(V2DF(100.0f, 100.0f), V2DF(50.0f, 0.0f), 1.0f, 0.0f, 300.0f, 0);

}

void Level::endLevel() {
	//release the current level
	int squadListSize = m_squadList.Get_Size();
	for(int i = 0; i < squadListSize; i++) {
		m_squadList.Get_Data(i)->Release();
		m_squadList.Remove_FromIndex(i);
	}
	t_sizeBulletList = playerBulletList.Get_Size();
	for(int i = 0; i < t_sizeBulletList; i++) {
		destroyBullet(playerBulletList,i);
	}
	t_sizeEnemyBulletList = enemyBulletList.Get_Size();
	for(int i = 0; i < t_sizeEnemyBulletList; i++) {
		destroyBullet(enemyBulletList,i);
	}

	if(m_Boss) {
		m_PhysicSystem.Destroy(&(m_Boss->m_PhysicObject));
		m_Boss->release();
		delete m_Boss;
		m_Boss = 0;
	}
	levelNow = 0;
	minionState = 0;
	bossSpawn = false;
	if(state == GAMEOVER) {
		gameover = false;
	}
	state = WAIT;
}

void Level::setBG(LPCWSTR file) {
	tex.initialize(file);
}

void Level::setMinions(int num, int x, int y, int dx, int dy, float scale, float rot, float speed, int dur, LPCWSTR file, LPCWSTR bulletfile) {
	Squad *t_squad = new Squad;
	t_squad->Initialize(&m_PhysicSystem, num, x, y, dx, dy, scale, rot, speed, dur, file, bulletfile);
	m_squadList.Insert_ToEnd(t_squad);
}

void Level::update(float dt) {
	switch(state) {
	case WAIT :
		state = START;
		break;

	case START :
		startLevel();
		state = PLAYING;
		break;

	case PLAYING :
		//////////////////////////////////////////////////////////////////////////////////////
		//// DEBUGGING P - to create Enemies, L - to delete all created enemies          ////           
		/////////////////////////////////////////////////////////////////////////////////////
		if(pInput->keyPressed(DIK_P)) {
			startLevel();
		}
		if(pInput->keyPressed(DIK_L)) {
			endLevel();
		}
		if(pInput->keyPressed(DIK_M)) {
			if(bgOn) {
				bgOn = false;
			}
			else {
				bgOn = true;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////
		/// ACTUAL GAME 
		//////////////////////////////////////////////////////////////////////////////////////

		switch(lvl) {
		case ONE:
			levelOne(dt);
			break;
		case TWO:
			levelTwo(dt);
			break;
		}
		break;
	case GAMEOVER:
		endLevel();
		break;
	}
}

void Level::render() {
	switch(state) {
	case WAIT:

		break;
	case START:
		break;
	case PLAYING:
		if(bgOn) {
			tex.draw(V2DF(400,300),0,3,true);
		}
		if(onScreen(p.m_PhysicObject)) {
			p.render();
		}

		for(int i = 0; i < playerBulletList.Get_Size(); i++){
			playerBulletList.Get_Data(i)->Draw();
		}
		for(int i = 0; i < m_squadList.Get_Size(); i++) {
			m_squadList.Get_Data(i)->Render();
		}
		if(bossSpawn) {
			if(m_Boss){
				m_Boss->render();
			}
		}
		/*	if(onScreen(e.m_PhysicObject)) {
		e.render();
		}*/

		break;
	}

	///////////////////////////////////////////////////////////
	// DEBUGGING 
	//////////////////////////////////////////////////////////
	char buff[500] = {0};
	RECT bounds;
	bounds = o.setBounds(0, 200, SCREEN_HEIGHT, SCREEN_WIDTH);
	sprintf_s(buff, "Time: %f Level: %d Lives: %d" , levelNow, lvl, p.getLives());
	pEngine->writeText(buff, bounds);

}

void Level::contact() {
	t_sizeSquadList = m_squadList.Get_Size();

	for(int i = 0; i < t_sizeSquadList; i++){
		contactBulletEnemy(playerBulletList, m_squadList.Get_Data(i)->m_enemyList);
		contactPlayerEnemy(p, m_squadList.Get_Data(i)->m_enemyList);
	}

	if(m_Boss){
		int t_BossBulletSize = m_Boss->m_BulletList.Get_Size();
		for(int i = 0; i < t_BossBulletSize; i++){
			if(p.m_PhysicObject.Contact(&m_Boss->m_BulletList.Get_Data(i)->m_PhysicObject)){
				p.m_PhysicObject.m_position.x -= 0.5f;
				//playerDie();
			}
		}
	}
}

void Level::updateBullets() {
	t_sizeBulletList = playerBulletList.Get_Size();
	//if player shoots, fire bullets
	if(p.getShoot()) {
		fireBullets();
		p.setShootToFalse();
	}

	//optimization: bullet offscreen = destroyed/released
	for(int i = 0; i < t_sizeBulletList; i++) {
		if(playerBulletList.Get_Data(i) ) {
			playerBulletList.Get_Data(i)->Update();
			if(!(onScreen(playerBulletList.Get_Data(i)->m_PhysicObject) )) {
				destroyBullet(playerBulletList,i);
			}
		}
	}
	//optimization: player bullets limited
	if(t_sizeBulletList > 1600){
		destroyBullet(playerBulletList,0);
	}
}

bool Level::onScreen(PhysicObject p) {
	if(p.m_position.x > 0 - WINDOWBUFF && p.m_position.x < SCREEN_WIDTH + WINDOWBUFF && p.m_position.y > 0 - WINDOWBUFF && p.m_position.y < SCREEN_HEIGHT + WINDOWBUFF) {
		return true;
	}
	else {
		return false;
	}
}

void Level::fireBullets() {
	if(p.getShootMode() == 1) {
		p.setShootSpeed(100);
		createBullet(V2DF(p.m_PhysicObject.m_position.x + BUFFER , p.m_PhysicObject.m_position.y),
			V2DF(4,0),0,0,400.0f,L"images/bullet2.png");
	}

	if(p.getShootMode() == 2) {
		p.setShootSpeed(10);
		createBullet(V2DF(p.m_PhysicObject.m_position.x + BUFFER, p.m_PhysicObject.m_position.y),
			V2DF(10.0f, (float)(rand()%3-1)+((float)(rand()%3-1)*0.5f)),
			0,0,rand()%100+400.0f,L"images/bullet2.png");
	}
}

void Level::createBullet(V2DF pos, V2DF vel, int scale, float rot, float speed, LPCWSTR file) {
	Bullet *t_Bullet = new Bullet;
	t_Bullet->Initialize(file);
	t_Bullet->Set_Data(pos,vel,scale,rot,speed);
	t_Bullet->m_PhysicObject.m_physic_object_type = PHYSIC_OBJECT_TYPE_CIRCLE;
	t_Bullet->m_PhysicObject.m_radius = 4.0f;
	playerBulletList.Insert_ToEnd(t_Bullet);	
	m_PhysicSystem.Create((&t_Bullet->m_PhysicObject));	
}

void Level::playerDie() {
	p.setLives(p.getLives() -1);
}

void Level::contactBulletEnemy(LinkedList<Bullet *> b,LinkedList<Enemy *>e) {
	for(int i = 0; i < b.Get_Size(); i++) {
		//b.Get_Data(i)->Update();
		for(int j = 0; j < e.Get_Size(); j++) {
			//e.Get_Data(j)->update();
			if(b.Get_Data(i)->m_PhysicObject.Contact(&(e.Get_Data(j)->m_PhysicObject))) {
				destroyEnemy(e,j);
				destroyBullet(b,i);
				break;
			}
		}
	}
	for(int i = 0; i < b.Get_Size(); i++){
		if(m_Boss){
			if(m_Boss->m_PhysicObject.Contact(&b.Get_Data(i)->m_PhysicObject)){
				m_Boss->setHP(m_Boss->getHP() - 1);
				destroyBullet(b,i);
			}
		}else{
			break;
		}
	}
}

void Level::destroyEnemy(LinkedList<Enemy *> e, int index) {
	m_PhysicSystem.Destroy(&(e.Get_Data(index)->m_PhysicObject));
	e.Get_Data(index)->m_PhysicObject.Release();
	e.Get_Data(index)->release();
	e.Remove_FromIndex(index);
}

void Level::destroyBullet(LinkedList<Bullet *> b, int index) {
	m_PhysicSystem.Destroy(&(b.Get_Data(index)->m_PhysicObject));
	b.Get_Data(index)->m_PhysicObject.Release();
	b.Get_Data(index)->Release();
	b.Remove_FromIndex(index);
}

void Level::contactPlayerEnemy(Player player, LinkedList<Enemy *> e) {
	for(int i = 0; i < e.Get_Size(); i++) {
		for(int j = 0; j < e.Get_Data(i)->m_BulletList.Get_Size(); j++){
			Bullet *t_enemyBullet = e.Get_Data(i)->m_BulletList.Get_Data(j);
			if(player.m_PhysicObject.Contact(&t_enemyBullet->m_PhysicObject)){
				playerDie();
				m_PhysicSystem.Destroy(&t_enemyBullet->m_PhysicObject);
				t_enemyBullet->Release();
				e.Get_Data(i)->m_BulletList.Remove_FromIndex(j);
				t_enemyBullet = 0;
			}
		}
		if(player.m_PhysicObject.Contact(&(e.Get_Data(i)->m_PhysicObject)) ) {
			playerDie();
			destroyEnemy(e,i);
		}
	}
	if(m_Boss){
		if(player.m_PhysicObject.Contact(&(m_Boss->m_PhysicObject)) ) {
			playerDie();
		}
		int t_sizeBossBullet = m_Boss->m_BulletList.Get_Size();
		for(int i = 0; i < t_sizeBossBullet; i++){
			if(player.m_PhysicObject.Contact(&(m_Boss->m_BulletList.Get_Data(i)->m_PhysicObject)) ) {
				m_PhysicSystem.Destroy(&m_Boss->m_BulletList.Get_Data(i)->m_PhysicObject);
				m_Boss->m_BulletList.Get_Data(i)->m_PhysicObject.Release();
				m_Boss->m_BulletList.Remove_FromIndex(i);
				m_Boss->m_BulletList.Get_Data(i)->m_PhysicObject;
				playerDie();
				break;
			}
		}
	}
}

void Level::moveMinion(Squad *a_squad,int x, int y, float dx, float dy) {
	if(a_squad) {
		for(int j = 0; j < a_squad->m_enemyList.Get_Size(); j++){
			a_squad->m_enemyList.Get_Data(j)->m_PhysicObject.m_velocity.set(dx, dy);
			a_squad->m_enemyList.Get_Data(j)->m_PhysicObject.m_position.set(
				a_squad->m_enemyList.Get_Data(j)->m_PhysicObject.m_position.x + j*x,
				a_squad->m_enemyList.Get_Data(j)->m_PhysicObject.m_position.y + j*y);
		}
	}
}

void Level::updateEnemy() {
	//check if the enemy is still on screen
	for(int i = 0; i < m_squadList.Get_Size(); i++) {
		m_squadList.Get_Data(i)->Update();
		for(int j = 0; j < m_squadList.Get_Data(i)->m_enemyList.Get_Size(); j++){
			if(m_squadList.Get_Data(i)->m_enemyList.Get_Data(j)) {
				if(!onScreen(m_squadList.Get_Data(i)->m_enemyList.Get_Data(j)->m_PhysicObject) ) {
					destroyEnemy(m_squadList.Get_Data(i)->m_enemyList, j);
				}
			}
		}
	}
}

void Level::levelOne(float dt) {
	levelNow += dt;

	m_PhysicSystem.Update(dt);
	p.update();


	if(bossSpawn) {
		m_Boss->update(dt);
	}

	updateBullets();
	updateEnemy();
	contact();

	if(levelNow >= 5 && minionState == 0) {
		setMinions(5,SCREEN_WIDTH,200,0,0,1,0,150,1,L"images/enemy_left.png", L"images/bullet2.png");
		moveMinion(m_squadList.Get_Data(0),BUFFER,0, -150,50);
		minionState ++;
	}
	if(levelNow >= 7 && minionState == 1) {
		moveMinion(m_squadList.Get_Data(0),0,0, -300,-50);
		minionState ++;
	}
	if(levelNow >= 8 && minionState == 2) {
		setMinions(5,SCREEN_WIDTH,400,0,0,1,0,150,1,L"images/enemy_left.png", L"images/bullet2.png");
		moveMinion(m_squadList.Get_Data(1),BUFFER,0, -250,0);		
		minionState ++;
	}
	if(levelNow >= 11  && minionState == 3) { 
		moveMinion(m_squadList.Get_Data(1),0,0, 50,0);
		minionState ++;
	}
	if(levelNow >= 15  && minionState == 4) { 
		setMinions(5,SCREEN_WIDTH,100,0,0,1,0,150,1,L"images/enemy_left.png", L"images/bullet2.png");
		setMinions(5,SCREEN_WIDTH,500,0,0,1,0,150,1,L"images/enemy_left.png", L"images/bullet2.png");
		moveMinion(m_squadList.Get_Data(2),BUFFER,0, -100,0);
		moveMinion(m_squadList.Get_Data(3),BUFFER,0, -100,0);
		minionState ++;
	}
	if(levelNow >= 20 && minionState == 5) { 
		moveMinion(m_squadList.Get_Data(2),0,0, 0,100);
		moveMinion(m_squadList.Get_Data(3),0,0, 0,-100);
		minionState ++;
	}
	if(levelNow >= 23 && minionState == 6) {
		setMinions(5,550,0,0,0,1,0,150,1,L"images/enemy_down.png", L"images/bullet2.png");
		moveMinion(m_squadList.Get_Data(4),0,BUFFER, 0,200);
		minionState ++;
	}
	if(levelNow >= 24 && minionState == 7) {
		setMinions(5,500,SCREEN_HEIGHT,0,0,1,0,150,1,L"images/enemy_up.png", L"images/bullet2.png");
		moveMinion(m_squadList.Get_Data(5),0,BUFFER, 0,-200);
		minionState ++;
	}
	if(levelNow >= 30 && minionState == 8) {
		setMinions(5,0,200,0,0,1,0,150,1,L"images/enemy_right.png", L"images/bullet2.png");
		setMinions(5,0,400,0,0,1,0,150,1,L"images/enemy_right.png", L"images/bullet2.png");
		moveMinion(m_squadList.Get_Data(6),-BUFFER,0, 100, 0);
		moveMinion(m_squadList.Get_Data(7),-BUFFER,0, 100, 0);
		minionState++;
	}


	if(levelNow >= 40 && minionState == 9) {
		setBoss();
		bossSpawn = true;
		minionState++;
	}

	if(levelNow >= 45 && minionState == 10){
		if(m_Boss){
			m_Boss->SET_AI(true);
		}
	}
	if(p.getLives() == 0) {
		state = GAMEOVER;
	}
	if(bossSpawn) { 
		if(m_Boss){
			if(!m_Boss->alive()/* && minionState == 10*/) {
				m_PhysicSystem.Destroy(&(m_Boss->m_PhysicObject));
				m_Boss->release();
				delete m_Boss;
				m_Boss = 0;
				lvl = TWO;
				endLevel();
			}
		}
	}
}

void Level::levelTwo(float dt) {
	levelNow += dt;

	m_PhysicSystem.Update(dt);
	p.update();

	if(bossSpawn) {
		if(m_Boss){
			m_Boss->update(dt);
		}
	}

	updateBullets();
	updateEnemy();
	contact();

	if(levelNow >= 1 && minionState == 0) { 
		minionState ++;
	}
	if(levelNow >= 2 && minionState == 1) {
		setMinions(5,800, 200,0,0,1,0,300,1,L"images/enemy_left.png", L"images/bullet2.png");
		moveMinion(m_squadList.Get_Data(0),BUFFER,0, -150,100);
		minionState ++;
	}
	if(levelNow >= 4 && minionState == 2) {
		moveMinion(m_squadList.Get_Data(0),0,0, -250,-100);
		minionState ++;
	}
	if(levelNow >= 6 && minionState == 3) {
		setMinions(5,700,400,0,0,1,0,300,1,L"images/enemy_left.png", L"images/bullet2.png");
		moveMinion(m_squadList.Get_Data(1),0,BUFFER, -150,0);
		minionState++;
	}
}

bool Level::gameOver() {
	return gameover;
}
